﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XmlStudio.DataLayer;
using XmlStudio.Enums;
using XmlStudio.Interfaces;
using XmlStudio.Utilities;

namespace XmlStudio.Models.Actions {
    internal sealed class MoveSubtreeAction : XmlModelAction {
        private BigXmlNodeInternal subtreeToMove;
        private BigXmlNodeInternal subtreeToMoveParent;
        private BigXmlNodeInternal subtreeToMoveLeftSibling;
        private bool subtreeToMoveModified;

        private BigXmlNodeInternal newParent;
        private bool newParentModified;
        private InsertMode insertMode;
        private BigXmlNodeInternal referenceNode;


        public MoveSubtreeAction(IXmlModelWithNotifications model, BigXmlNodeInternal subtreeToMove, BigXmlNodeInternal newParent, InsertMode insertMode, BigXmlNodeInternal referenceNode)
            : base(model) {
            this.subtreeToMove = subtreeToMove;
            this.newParent = newParent;
            this.insertMode = insertMode;
            this.referenceNode = referenceNode;

            this.subtreeToMoveParent = this.subtreeToMove.Parent as BigXmlNodeInternal;
            this.subtreeToMoveLeftSibling = this.subtreeToMoveParent.GetLeftSibling(subtreeToMove);
            this.subtreeToMoveModified = this.subtreeToMove.Modified;
            this.newParentModified = this.newParent.Modified;
        }

        protected override void ExecuteCore() {
            subtreeToMove.SetParent(newParent);
            subtreeToMoveParent.ChildNodes.Remove(subtreeToMove);

            switch(this.insertMode) {
                case InsertMode.First:
                    newParent.ChildNodes.Insert(0, subtreeToMove);
                    newParent.XmlNode.InsertBefore(subtreeToMove.XmlNode, newParent.XmlNode.FirstChild);
                    break;
                case InsertMode.After:
                    newParent.ChildNodes.Insert(newParent.ChildNodes.IndexOf(referenceNode), subtreeToMove);
                    newParent.XmlNode.InsertAfter(subtreeToMove.XmlNode, referenceNode.XmlNode);
                    break;
                case InsertMode.Before:
                    newParent.ChildNodes.Insert(newParent.ChildNodes.IndexOf(referenceNode) - 1, subtreeToMove);
                    newParent.XmlNode.InsertBefore(subtreeToMove.XmlNode, referenceNode.XmlNode);
                    break;
                case InsertMode.Last:
                    newParent.ChildNodes.Add(subtreeToMove);
                    newParent.XmlNode.AppendChild(subtreeToMove.XmlNode);
                    break;
            }

            TreeBuilder.AdjustAddresses(subtreeToMoveParent, this.model);
            TreeBuilder.AdjustAddresses(newParent, this.model);
            this.subtreeToMove.SetModified(true);
            this.newParent.SetModified(true);

            this.model.SetModified(true);
            this.model.OnSubtreeUpdated(new Events.SubtreeUpdatedEventArgs<BigXmlNode>(subtreeToMoveParent));
            this.model.OnSubtreeUpdated(new Events.SubtreeUpdatedEventArgs<BigXmlNode>(newParent));
            this.model.OnRawXmlChanged(new Events.RawXmlChangedEventArgs());
        }

        protected override void UnExecuteCore() {
            subtreeToMove.SetParent(subtreeToMoveParent);
            newParent.ChildNodes.Remove(subtreeToMove);

            if(subtreeToMoveLeftSibling != null) {
                subtreeToMoveParent.ChildNodes.Insert(subtreeToMoveParent.ChildNodes.IndexOf(subtreeToMoveLeftSibling), subtreeToMove);
                subtreeToMoveParent.XmlNode.InsertAfter(subtreeToMove.XmlNode, subtreeToMoveLeftSibling.XmlNode);
            } else {
                subtreeToMoveParent.ChildNodes.Insert(0, subtreeToMove);
                subtreeToMoveParent.XmlNode.InsertBefore(subtreeToMove.XmlNode, subtreeToMoveParent.XmlNode.FirstChild);
            }

            TreeBuilder.AdjustAddresses(subtreeToMoveParent, this.model);
            TreeBuilder.AdjustAddresses(newParent, this.model);
            this.subtreeToMove.SetModified(subtreeToMoveModified);
            this.newParent.SetModified(newParentModified);

            //this.xmlModel.OnModelModified(new Events.ModelModifiedEventArgs(this.modelWasModified));
            this.model.SetModified(this.modelWasModified);
            this.model.OnSubtreeUpdated(new Events.SubtreeUpdatedEventArgs<BigXmlNode>(newParent));
            this.model.OnSubtreeUpdated(new Events.SubtreeUpdatedEventArgs<BigXmlNode>(subtreeToMoveParent));
            this.model.OnRawXmlChanged(new Events.RawXmlChangedEventArgs());
        }
    }
}
